def f(L,a):
return 60*L**a
5 - sympy
Sympy er en veldig nyttig pakke innenfor samfunnsøkonomi. Med den kan vi regne analytisk, det vil si med symboler. I samfunnsøkonomi bruker vi mye matematikk som denne pakken kan hjelpe oss med. Vi starter med et eksempel på utregning av profittmaksimum:
Eksempel med optimal mengde arbeidskraft
Anta at du er bedriftsleder, og lurer på hvor mange medarbeidere du skal ansette. For en gitt mengde arbeidskraft produseres
Eksempel 1:
a
definerer produktiviteten til de ansatte. Desto høyere a
er, desto mer produktive er de ansatte. Vi kan plotte denne funksjonen:
Eksempel 2:
import matplotlib.pyplot as plt
import numpy as np
= np.linspace(0,5,100)
x
0.5))
plt.plot(x,f(x,'Arbeidskraft')
plt.xlabel('Produksjon') plt.ylabel(
Text(0, 0.5, 'Produksjon')
Som vi ser er produktiviteten avtakende, kurven stiger mindre utover i diagrammet. Det er fordi a
er mindre enn én. (Forsøk med andre tall for a
!)
Når produktfunksjonen er definert, kan vi definere fortjenesten til bedriften, eller “profittfunksjonen” som vi gjerne kaller det i samfunnsøkonomi. La oss si at bedriften betaler hver arbeider w
tusen kroner, og at faste kostnader er K
.
Fortenesten er pris ganger solgt mengde, p*f(L,a)
og kostnadene er lønnskostnader w*L
og faste utgifter K
. Profittfunksjonen blir da
Eksempel 3:
def profit(L,a,w,p,K):
return p*f(L,a)-w*L-K
La oss se på den grafisk. Dersom prisen per enhet er hundre kroner, lønna er 2 500 per dag og bedriften har 300 000 i faste utgifter per dag, så ser profittfunksjonen slik ut:
Eksempel 4:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.pyplot as plt
import numpy as np
#creating the plot
= np.linspace(0,5000,100)
x =plt.subplots()
fig,ax'kroner')
ax.set_ylabel('Antall ansatte')
ax.set_xlabel(
#plotting the function
0.9,2500,100,300000),label='Fortjeneste')
plt.plot(x,profit(x,='lower right',frameon=False) ax.legend(loc
<matplotlib.legend.Legend at 0x12a65fd3a10>
Vi bruker metoden med fig,ax=plt.subplots()
, siden vi skal bygge på denne grafen.
Vi skal nå begynne å bruke sympy. Det første vi må gjøre er å definere hvilke symboler som skal behandles analytisk (altså som symboler og ikke flyttall). Det gjør vi slik:
Eksempel 5:
import sympy as sp
=sp.symbols("L a w p K") L,a,w,p,K
Med symbolene definert, vil nå vår profittfunksjon vises analytisk når vi bruker de definere symbolene:
Eksempel 6:
profit(L,a,w,p,K)
\(\displaystyle - K - L w + 60 L^{a} p\)
Vi ønsker å finne ut for hvilken arbeidskraft fortjenesten er størst. Det er det høyeste punktet i figuren fra Eksempel 3. På dette punktet har profittfunksjonen ingen stigning, slik at den deriverte er null. For å finne dette punktet må vi først finne den deriverte. Det gjør vi i sympy med funksjonen diff()
. Den tar to argumenter. Det første er funksjonen, det andre er den variabel vi ønsker å derivere med hensyn til.
Som vi ser av figuren i Eksempel 3, så har vi arbeidskraft L
langs x-aksen, så det er denne variabelen vi ønsker å derivere med hensyn til. Den deriverte av profitt()
med hensyn til L
er dermed:
Eksempel 7:
=sp.diff(profit(L,a,w,p,K),L)
d_profitt d_profitt
\(\displaystyle - w + \frac{60 L^{a} a p}{L}\)
For å finne punktet der denne deriverte er null, setter vi opp en ligning der den deriverte er null, og løser for den L
som tilfredstiller ligningen. En slik ligning kalles “førsteordensbetingelse”, eller “first order condition” på engelsk. Vi forkorter den derfor til “foc”:
Eksempel 8:
=sp.Eq(d_profitt,0)
foc foc
\(\displaystyle - w + \frac{60 L^{a} a p}{L} = 0\)
Vi kan nå løse førsteordensbetingelsen med funksjonen solve
, som ligger i modulen solvers
i sympy:
Eksempel 9:
from sympy.solvers import solve
=solve(foc,L)[0]
L_max L_max
\(\displaystyle \left(\frac{60 a p}{w}\right)^{- \frac{1}{a - 1}}\)
Legg merke til at resultatet ligger i en liste med lengde 1, så vi må hente ut element 0 i listen for å vise resultatet. Vi kan finne hva den analytiske verdien er i maksimum ved å sette L_max
inn i profittfunksjonen:
Eksempel 10:
=profit(L_max,a,w,p,K)
profit_max profit_max
\(\displaystyle - K + 60 p \left(\left(\frac{60 a p}{w}\right)^{- \frac{1}{a - 1}}\right)^{a} - w \left(\frac{60 a p}{w}\right)^{- \frac{1}{a - 1}}\)
Vi kan nå beregne de nummeriske verdiene ved å sette inn noen passende tall for de ukjente symbolene. Vi prøver med 0.5 for produktivitet a
, 0.3 for lønn w
, 1 for pris p
og 1 for kapital K
. Vi forteller sympy om at vi ønsker å bruke dise verdiene ved å lage et oppslag der hvert av symbolene er nøkkel til hver av de nummeriske verdiene:
Eksempel 11:
={a:0.9,w:2500,p:100,K:300000}
num_dict num_dict
{a: 0.9, w: 2500, p: 100, K: 300000}
Vi kan nå finne nummerisk hvor mye arbeidskraft som trengs for å oppnå maksimal fortjeneste:
Eksempel 12:
L_max.subs(num_dict)
\(\displaystyle 2210.73919720734\)
Om vi nå legger dette tallet inn for symbol L
, kan vi finne hvor stor fortjenesten er i dette punktet. Vi starter med å legge inn verdien for L som gir maksimal fortjeneste:
Eksempel 13:
=L_max.subs(num_dict)
num_dict[L] num_dict
{a: 0.9, w: 2500, p: 100, K: 300000, L: 2210.73919720734}
Med det oppdaterte oppslaget blir fortjenesten
Eksempel 14:
=float(profit(L,a,w,p,K).subs(num_dict))
profit_max_num profit_max_num
314094.22144648153
Med modulene display
og Markdown
fra IPython
(pakken som driver Jupyter), kan vi sette dette in i en pen tabell:
Eksempel 15:
from IPython.display import Markdown
=f"""
tbl| | Desimalverdi | Analytisk verdi |
| :-------------------------------| :----------------------------------------| :-----------------------------|
| Optimal mengde arbeidskraft: | ${np.round(float(num_dict[L]),1)}$ |${sp.latex(L_max)}$ |
| Maksimal profitt | ${np.round(float(profit_max_num),1)}$ |${sp.latex(profit_max)}$ |
"""
display(Markdown(tbl))
Desimalverdi | Analytisk verdi | |
---|---|---|
Optimal mengde arbeidskraft: | \(2210.7\) | \(\left(\frac{60 a p}{w}\right)^{- \frac{1}{a - 1}}\) |
Maksimal profitt | \(314094.2\) | \(- K + 60 p \left(\left(\frac{60 a p}{w}\right)^{- \frac{1}{a - 1}}\right)^{a} - w \left(\frac{60 a p}{w}\right)^{- \frac{1}{a - 1}}\) |
La oss nå plotte løstningen. Vi plotter her følgende, i rekkefølge: 1. Den opprinnelige profittfunksjonen 2. Den horisontale tangenten som tangerer i maksimumspunktet 3. En vertikal linje som viser mengden arbeidskraft i optimum.
Legg merke til at vi legger inn verdiene vi har definert i num_dict
inn i profittfunksjonen. Generelt er det en god idé i programmering å “hardkode” tall minst mulig. Definer det heller tallet som en variabel eller element i en dict
og referer til det senere.
Eksempel 16:
#Plotting the tangent
ax.plot(x,*np.ones(len(x)),
profit_max_num='tangent')
label
#Plotting the vertical line
float(L_max.subs(num_dict)), #x-value
ax.vlines(-300000, #min y-value
*1.1, #max y-value
profit_max_num#formatting:
=['black'],
colors='dashed',
linestyles='optimum')
label
='lower right',frameon=False)
ax.legend(loc0, color='black',lw=0.1)
ax.axhline( fig
Eksempel med tilbud og etterspørsel
I forelesning 3 definerte vi disse tilbuds og etterspørselsfunksjonene:
Eksempel 17:
def supply(x):
return (x**2)*(1/250)
def demand(x):
return 3000/(100+x)
Og vi tegnet dem slik:
Eksempel 18:
#drawing 100 points in the interval 0.0 to 100
= np.linspace(1,100,100)
q
#creating the plot
=plt.subplots()
fig,ax'Pris')
ax.set_ylabel('Enheter')
ax.set_xlabel(
#drawing supply
='Tilbud')
ax.plot(q,supply(q),label
#drawing demand
='green',label='Etterspørsel')
ax.plot(q,demand(q),color
#adding legend:
='upper center',frameon=False) ax.legend(loc
<matplotlib.legend.Legend at 0x12a66059f90>
Vi løste da likevekten grafisk, ved å se sånn cirka hvor tilbud er lik etterspørsel. Med sympy kan vi la python regne ut dette, og konsument og produsentoverskudd. Vi gjør dette ved å definere mengde x
som en eksogen variabel, og sette opp ligningen vi trenger, altså at tilbud skal være lik etterspørsel:
Eksempel 19:
=sp.symbols('x')
x=sp.Eq(demand(x),supply(x))
eq_cond eq_cond
\(\displaystyle \frac{3000}{x + 100} = 0.004 x^{2}\)
Vi kan nå løse dette med solve fra sympy, som i forrige eksempel:
Eksempel 20:
=solve(eq_cond,x)
x_eq x_eq
[67.0125415064549,
-83.5062707532274 - 64.9510401355397*I,
-83.5062707532274 + 64.9510401355397*I]
Bare én av disse løsningene er gyldige. De to siste i listen x_eq
er såkalte “imaginære tall”, det ser vi av I
’en. Vi går ikke lenger inn på hva dette er her, men nøyer oss med å si at en likevekt ikke kan være et imaginært tall. Løsningen er altså x_eq[0]
. Vi kan sette denne inn i enten tilbuds eller etterspørselfunksjonen for å få likevektsprisen
Eksempel 21:
=demand(x_eq[0])
p_eqprint(f"""
Likevektspris er {p_eq}
Likevektskvantum er {x_eq[0]}
""")
Likevektspris er 17.9627228766173
Likevektskvantum er 67.0125415064549
Etterspørselskurven kan ses på som en rekke med konsumenter med ulik betalingsvilje i fallende rekkefølge. Alle konsumentene som betaler p_eq
har dermed et overskudd som er lik differansen mellom p_eq
og konsumentens punkt på etterspørselskurven. Summen av alle konsumentenes overskudd kalles konsumentoverskuddet. Dette kan illustreres ved å legge et skravert område til figuren over
Eksempel 22:
= np.linspace(0,float(x_eq[0]),100)
q #x-values
ax.fill_between(q, float(p_eq), #Eqilibrium price
#y-values
demand(q), #formatting:
= "pink",alpha = 0.3,label='Konsumentoverskudd')
color ='upper center',frameon=False)
ax.legend(loc fig
Akkrat som at vi kan regnet ut skjæringspunktet med sympy, så kan vi regne ut det skraverte konsumentoverskuddet. Vi bruker da det vi har lært i matematikkurset; arealet under en funksjon er integralet til funksjonen. Vi skal finne arealet under etterspørselsfunksjonen demand(x)
, men kun ned til prisen p_eq
, så vi integrer differansen demand(x)-p_eq
. Dette gjør vi for alle omsatte enheter, altså frem til omsatt kvantum x_eq[0]
.
Vi skal altså integrere demand(x)-p_eq
i intervalet 0 til x_eq[0]
. Det kan vi gjøre i sympy slik:
Eksempel 23:
=sp.integrate(demand(x)-float(p_eq),(x,0,x_eq[0]))
consumer_surplus consumer_surplus
\(\displaystyle 334.968455011534\)
På samme måte er produsentoverskuddet arealet over tilbuskurven, opp til prisen, altså det gule området i figuren under
Eksempel 24:
float(p_eq), color = "yellow",alpha = 0.3,label='Produsentoverskudd')
ax.fill_between(q,supply(q),float(x_eq[0]), 0, 25,colors=['black'],linestyles='dashed', label='x_eq[0]')
ax.vlines(='upper center',frameon=False)
ax.legend(loc fig
Vi kan regne ut dette område også, som altså er integralet av differansen mellom prisen og tilbudskruven, frem til x_eq[0]
.
Eksempel 25:
=sp.integrate(p_eq-supply(x),(x,0,x_eq[0]))
producer_surplus producer_surplus
\(\displaystyle 802.485141558844\)
Summen av produsentoverskuddet og konsumentoversdkuddet kalles “velferdsgevinsten”. Vi kan finne den ved å legge sammen konsument- og produsentoverskudd:
Eksempel 26:
+consumer_surplus producer_surplus
\(\displaystyle 1137.45359657038\)
Eller ved å ta integralet av differansen mellom etterspørsel og tilbud:
Eksempel 27:
=sp.integrate(demand(x)-supply(x),(x,0,x_eq[0]))
welfare_surplus welfare_surplus
\(\displaystyle 1137.45359657038\)
Vi kan nå lage en tabell som oppsumerer resultatene:
Eksempel 28:
=f"""
tbl| | Verdi |
| :-------------------| :----------------------------------------|
| Solgt mengde: | ${np.round(float(x_eq[0]),1)}$ |
| Pris: | ${np.round(float(p_eq),1)}$ |
| Verdiskapning/<br>velferdsgevinst: | ${np.round(float(welfare_surplus),1)}$ |
| Konsumentoverskudd: | ${np.round(float(consumer_surplus),1)}$ |
| Produsentoverskudd: | ${np.round(float(producer_surplus),1)}$ |
"""
display(Markdown(tbl))
Verdi | |
---|---|
Solgt mengde: | \(67.0\) |
Pris: | \(18.0\) |
Verdiskapning/ velferdsgevinst: |
\(1137.5\) |
Konsumentoverskudd: | \(335.0\) |
Produsentoverskudd: | \(802.5\) |
Løse flere ligninger med flere ukjente
I eksempel 20 hadde vi én ligning med én ukjent. Det er imidlertid enkelt å løse flere ligninger med flere ukjente. Forskjellen er bare at når det er flere ligninger, så setter vi hver av disse inn i en liste. Som dere vet må det være akkurat like mange ukjente som ligninger, så vi må også sette akkurat like mange variabler inn i en annen liste. Disse to listene utgjør så argumentene i solve()
funksjonen. La oss ta et eksempel. Vi begynner med å definere to lister med henholdsvis tre variabler og tre ligninger:
Eksempel 29:
import sympy as sp
=sp.symbols("x y z")
x,y,z=[x,y,z]
symbols=[
equations2*x+8*y+3*z,7),
sp.Eq(-3*x+15*y-14*z,-20),
sp.Eq(11*x-6*y+7*z,35)
sp.Eq( ]
Vi setter så disse to listene inn i solve()
akkurat som om det var én ligning og én ukjent, og finner svaret:
Eksempel 30
solve(equations,symbols)
{x: 3868/1463, y: -6/77, z: 1139/1463}
Oppgaver/Arbeidskrav:
Her skal dere løse matematikkoppgaver med Sympy, og bruke fordelene som programmering gir til å løse oppgavene så effektivt som mulig. I stedet for å løse hver oppgave individuelt, skal dere derfor lage funksjoner som tar uttrykkene som argumenter, og så løse hver oppgave.
Likningssett - faktoriseringsmetoden (klikk her for å laste ned oppgavene)
Lag en funksjon test_solve(eqs)
der eqs er et Sympy ligningssett, og der du bruker solve(f)
til å finne løsning på likhetene. Lag en test i funksjonen, ved å sette løsningen inn i likheten. Funksjonen skal returnere både svaret og resultatet av løsningen.
from sympy.solvers import solve
import sympy as sp
=sp.symbols("x y z")
x,y,z
def test_solve(eqs):
= solve(eqs)
s #testing equation for each element in eqs
= [eq.subs(s[0]) for eq in eqs]
tests print(s, tests)
return s, tests
test_solve([*y**2-x,0),
sp.Eq(x+y**2,16) ])
sp.Eq(x
test_solve([**2+y**2,0),
sp.Eq(x**2*y-36*y,0) ])
sp.Eq(x
test_solve([*y**2-49*x,0),
sp.Eq(x**2+y**2,58) ])
sp.Eq(x
test_solve([**2+y**2,5/4),
sp.Eq(x2*x*y+y,0) ]) sp.Eq(
[{x: 0, y: -4}, {x: 0, y: 4}, {x: 15, y: -1}, {x: 15, y: 1}] [True, True]
[{x: -6, y: -6*I}, {x: -6, y: 6*I}, {x: 0, y: 0}, {x: 0, y: 0}, {x: 6, y: -6*I}, {x: 6, y: 6*I}] [True, True]
[{x: -3, y: -7}, {x: -3, y: 7}, {x: 0, y: -sqrt(58)}, {x: 0, y: sqrt(58)}, {x: 3, y: -7}, {x: 3, y: 7}] [True, True]
[{x: -1.11803398874989, y: 0.0}, {x: -0.500000000000000, y: -1.00000000000000}, {x: -0.500000000000000, y: 1.00000000000000}, {x: 1.11803398874989, y: 0.0}] [False, True]
([{x: -1.11803398874989, y: 0.0},
{x: -0.500000000000000, y: -1.00000000000000},
{x: -0.500000000000000, y: 1.00000000000000},
{x: 1.11803398874989, y: 0.0}],
[False, True])
Grunnleggende derivasjonsregler (klikk her for å laste ned oppgavene)
Løs alle oppgavene med Sympy i én celle. Bruk diff()
-funksjonen for å derivere, og gjør koden så kort som mulig (i antall tegn, ikke antall linjer).
for f in [x**5,
+12,
x**3+4*x**2,
x2*x**3+36**0.5,
**0.5+5**2,
x**3+x**-1,
x**3-x**2+x/4,
x1/x+6*x**6,
-x+(x**3)/2,
*(x**2+2*x-3)
x
]:print(sp.diff(f))
5*x**4
1
3*x**2 + 8*x
6*x**2
0.5/x**0.5
3*x**2 - 1/x**2
3*x**2 - 2*x + 1/4
36*x**5 - 1/x**2
3*x**2/2 - 1
x**2 + x*(2*x + 2) + 2*x - 3
Produkt og brøkregelen (klikk her for å laste ned oppgavene)
1)
Forenkl uttrykkene i Oppgavene 1 a)-d) med færrest mulig tegn. Kommenter hvilke av oppgavene som lar seg forenkle.
for f in [x**(12/3), (x**2+4)**(8/2), sp.sqrt(x)*x**(3/2), ((x+2)**(1/2))**(4/2)]:
print(sp.simplify(f))
x**4.0
(x**2 + 4)**4.0
x**2.0
(x + 2)**1.0
Kommentar: forenklingsfunksjonen i sympy gir oss egentlig ikke så mye her
2)
Lag to funksjoner f_deriv_prod(f, g)
og f_deriv_frac(f, g)
som bruker henholdsvis produktregelen og brøkregelen til å derivere et uttrykk. Argumenter skal være de to delene i produktet. Om produktet for eksempel er \(\sqrt{3x-1}\cdot(1+x)\) skal argumentene være \(\sqrt{3x-1}\) og \((1+x)\). Funksjonen skal returnere den deriverte av produktet.
Deriver alle utrykene i e)-m) med disse funksjonene, men velg først ut alle produktene og deriver de, før du i neste omgang deriverer brøkene.
def f_deriv_prod(f,g):
= sp.diff(f)
df = sp.diff(g)
dg
return df*g + f*dg
def f_deriv_frac(f,g):
= sp.diff(f)
df = sp.diff(g)
dg
return df/g - f*dg/g**2
#product rule
for f,g in [
**4, x**5),
(x
(x, sp.sqrt(x)), **6),
(sp.sqrt(x), x*(x**2+1)),
(x, x
]:print(f"derivative: {f_deriv_prod(f,g)}\t\t\ttest: {sp.simplify(f_deriv_prod(f,g)-sp.diff(f*g))} " )
#fraction rule
for f,g in [
2, x**3),
(**2 - 2, x**3),
(x*2 + 4, x**3 - 1),
(x**2, x**2 + 2*x),
(x**2 + 4*x + 2, x - 2),
(x
]:print(f"derivative: {f_deriv_frac(f,g)}\t\t\ttest: {sp.simplify(f_deriv_frac(f,g)-sp.diff(f/g))} " )
derivative: 9*x**8 test: 0
derivative: 3*sqrt(x)/2 test: 0
derivative: 13*x**(11/2)/2 test: 0
derivative: x*(x**2 + 1) + x*(3*x**2 + 1) test: 0
derivative: -6/x**4 test: 0
derivative: 2/x**2 - 3*(x**2 - 2)/x**4 test: 0
derivative: -3*x**2*(2*x + 4)/(x**3 - 1)**2 + 2/(x**3 - 1) test: 0
derivative: -x**2*(2*x + 2)/(x**2 + 2*x)**2 + 2*x/(x**2 + 2*x) test: 0
derivative: (2*x + 4)/(x - 2) - (x**2 + 4*x + 2)/(x - 2)**2 test: 0
3)
Test at produktregelen fungerer ved å printe differansen mellom funksjonene over og derivering av hele uttrykket med sp.diff()
. Du kan sette dette inn i koden over om du vil.
Kjerneregelen (klikk her for å laste ned oppgavene)
1)
Lag en funksjon f_deriv_chain(f, g)
som bruker kjerneregelen til å derivere et uttrykk av typen \(f(g(x))\).
Har du for eksempel et uttrykk \(\sqrt{3x-1}\), så er \(f(y)=\sqrt{y}\) og \(g(x)=\sqrt{3x-1}\). Funksjonen skal da returnere \(f'(g(x))\cdot g'(x)=\frac{1}{2}\frac{1}{\sqrt{3x-1}}\cdot 3\)
Husk å bytt ut \(y\) med \(g(x)\) (i dette tilfelle \(3x-1\)) før svaret returneres.
def f_deriv_chain(f, g):
= sp.diff(f)
df = sp.diff(g)
dg
return df.subs({'y':g})*dg
2)
Bruk
f_deriv_chain(f, g)
på uttrykkene i Oppgave 1 a), b), d), e) og Oppgave 2 a) og e).Bruk
f_deriv_prod(f, g)
i kombinasjon medf_deriv_chain(f(y), g(x))
og løs Oppgave 1 h) og Oppgave 2 b)
Test samtidig resultatene med sp.diff())
, der du stter inn kjernen i funksjonen før du deriverer.
for f, g in [
**5, x**3 + 6*x),
(y2*x**2 + 5),
(sp.sqrt(y), **(3/2), 2*x**4 + 9),
(y**4), 3*x**2 + 2*x)
(sp.sqrt(y
]:
print(f"derivative: {f_deriv_chain(f,g)}\t\t\ttest: {sp.simplify(f_deriv_chain(f,g)-sp.diff( f.subs({'y':g}) ))} " )
derivative: 5*(3*x**2 + 6)*(x**3 + 6*x)**4 test: 0
derivative: 2*x/sqrt(2*x**2 + 5) test: 0
derivative: 12.0*x**3*(2*x**4 + 9)**0.5 test: 0
derivative: 2*(6*x + 2)*sqrt((3*x**2 + 2*x)**4)/(3*x**2 + 2*x) test: 0
Derivasjon av logaritme og eksponentialfunksjoner (klikk her for å laste ned oppgavene)
Løs følgende oppgaver: Oppgave 1 a), b), f) og Oppgave 2 a) og b). Lag koden med så få tegn som mulig-
for f in [
5*x),
sp.log(-2*x),
sp.exp(4*x**2 + 8*x),
sp.exp(**3 * sp.exp(2*x),
x**2-1)/(x**2 + 1))
sp.log((x
]:print(sp.diff(f))
1/x
-2*exp(-2*x)
(8*x + 8)*exp(4*x**2 + 8*x)
2*x**3*exp(2*x) + 3*x**2*exp(2*x)
(x**2 + 1)*(-2*x*(x**2 - 1)/(x**2 + 1)**2 + 2*x/(x**2 + 1))/(x**2 - 1)